package org.seamon.management.util.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.seamon.management.pojo.entity.User;
import org.seamon.management.service.UserRoleService;
import org.seamon.management.util.enums.ErrorCodeEnum;
import org.seamon.management.util.exception.BusinessException;
import org.seamon.management.util.security.UserContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.List;

@Aspect
@Component
public class DataPermissionAspect {
    private final UserRoleService userRoleService;
    private final SpelExpressionParser parser = new SpelExpressionParser();
    private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    public DataPermissionAspect(UserRoleService userRoleService) {
        this.userRoleService = userRoleService;
    }

    @Around("@annotation(dataPermission)")
    public Object around(ProceedingJoinPoint joinPoint, DataPermission dataPermission) throws Throwable {
        // 获取当前登录用户
        User currentUser = UserContext.getCurrentUser();

        // 跳过管理员校验
        if (isAdmin(currentUser)) {
            return joinPoint.proceed();
        }

        // 获取目标用户ID
        Long targetUserId = getTargetUserId(joinPoint, dataPermission);

        // 执行权限校验
        if (dataPermission.selfOnly() && !currentUser.getId().equals(targetUserId)) {
            throw new BusinessException(ErrorCodeEnum.FORBIDDEN);
        }

        return joinPoint.proceed();
    }

    /**
     * 获取目标用户ID
     */
    private Long getTargetUserId(ProceedingJoinPoint joinPoint, DataPermission dataPermission) {
        // 优先使用 SpEL 表达式
        if (!dataPermission.userIdSpel().isEmpty()) {
            return getTargetUserIdBySpel(joinPoint, dataPermission.userIdSpel());
        }

        // 其次使用参数名
        if (!dataPermission.userIdParam().isEmpty()) {
            return getTargetUserIdByParam(joinPoint, dataPermission.userIdParam());
        }

        throw new IllegalArgumentException("请配置 userIdSpel 或 userIdParam");
    }

    /**
     * 通过 SpEL 表达式获取目标用户ID
     */
    private Long getTargetUserIdBySpel(ProceedingJoinPoint joinPoint, String spel) {
        EvaluationContext context = createEvaluationContext(joinPoint);
        Expression expression = parser.parseExpression(spel);
        Object value = expression.getValue(context);
        return convertToLong(value);
    }

    /**
     * 通过参数名获取目标用户ID
     */
    private Long getTargetUserIdByParam(ProceedingJoinPoint joinPoint, String paramName) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] paramNames = parameterNameDiscoverer.getParameterNames(signature.getMethod());
        Object[] args = joinPoint.getArgs();

        for (int i = 0; i < paramNames.length; i++) {
            if (paramNames[i].equals(paramName)) {
                Object arg = args[i];
                if (arg instanceof Long) {
                    return (Long) arg;
                } else if (arg != null) {
                    return getFieldRecursive(arg, "id");
                }
            }
        }
        throw new IllegalArgumentException("无法找到参数：" + paramName);
    }

    /**
     * 创建 SpEL 上下文
     */
    private EvaluationContext createEvaluationContext(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] paramNames = parameterNameDiscoverer.getParameterNames(signature.getMethod());
        Object[] args = joinPoint.getArgs();

        StandardEvaluationContext context = new StandardEvaluationContext();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }
        return context;
    }

    /**
     * 递归获取对象字段值（支持嵌套属性）
     */
    private Long getFieldRecursive(Object obj, String fieldName) {
        try {
            String[] fields = fieldName.split("\\.");
            Object current = obj;
            for (String field : fields) {
                Field f = current.getClass().getDeclaredField(field);
                f.setAccessible(true);
                current = f.get(current);
                if (current == null) return null;
            }
            return convertToLong(current);
        } catch (Exception e) {
            throw new IllegalArgumentException("无法从对象中获取字段: " + fieldName, e);
        }
    }

    /**
     * 转换为 Long
     */
    private Long convertToLong(Object value) {
        if (value == null) return null;
        if (value instanceof Number) {
            return ((Number) value).longValue();
        } else if (value instanceof String) {
            return Long.valueOf((String) value);
        } else {
            throw new IllegalArgumentException("不支持的数据类型: " + value.getClass());
        }
    }

    /**
     * 判断是否为管理员
     */
    private boolean isAdmin(User user) {
        List<Long> roles = userRoleService.getRole(user.getId());
        return roles.contains(1L) || roles.contains(2L);
    }
}
